package com.lz.ub

import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.ArrayMap
import android.util.SparseArray
import com.felhr.usbserial.UsbSerialDevice
import com.felhr.usbserial.UsbSerialInterface
import com.lz.ub.core.UBCHandler
import com.lz.ub.core.UBCScanResult
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean

/**
 * @author You
 * 核心USB Command指令调度器, 在子线程中操作即调度器就是一个线程类, 可用此类直接使用, 也可使用[UBCManager]便捷使用
 * @param isAssistDispatcher 是否为辅助设备调度器
 */
internal class UBCDispatcher(
    private val usbDevice: UsbDevice,
    private val usbManager: UsbManager,
    private val baudRate: Int,
    private val dataBits: Int,
    private val stopBits: Int,
    private val parity: Int,
    private val factory: UBCReader.Factory,
    val isAssistDispatcher: Boolean = false,
    private val callbackExecute: UBCCallbackExecute
) : Thread("UBCDispatcher-Thread ${usbDevice.deviceId}") {

    companion object {

        const val TAG = "UBCDispatcher"

        //关闭蓝牙主板
        const val MSG_CLOSE = 1

        //添加设备
        const val MSG_ADD_DEVICE = 2

        //添加设备列表
        const val MSG_ADD_DEVICE_LIST = 3

        //重启
        const val MSG_RESTART = 4

        //连接所有
        const val MSG_CONNECT_ALL = 5

        //断开所有
        const val MSG_DISCONNECT_ALL = 6

        //取消重连并断开所有
        const val MSG_CANCEL_ALL = 7

        //发送16进制消息 to 所有设备
        const val MSG_HEX_COMMAND_ALL = 8

        //发送16进制消息, 单个设备单
        const val MSG_HEX_COMMAND = 9

        //发送16进制消息, 多个设备
        const val MSG_HEX_COMMAND_MULTIPLE = 10

        //扫描与停止扫描
        const val MSG_SCANNER = 11

        const val MSG_CLEAR = 12
    }

    //当前线程的looper
    private lateinit var looper: Looper

    //当前线程消息处理器
    internal lateinit var h: H

    //是否已经启动
    private var hasStart = AtomicBoolean(false)

    //指令执行器
    private lateinit var operator: UBCOperator

    //默认一个最大8台
    private val devices = ArrayMap<String, UBCDevice>(MAX_DEVICE_SIZE)

    //设备异步锁, 只在添加删除操作时上锁
    private val deviceSync = Any()

    //已经连接上的设备
    private val connectedDevices = SparseArray<UBCDevice>(MAX_DEVICE_SIZE)

    //当前所有设备的mac
    private val macs = HashSet<String>(MAX_DEVICE_SIZE)

    //当前主板是否打开
    fun isOpened(): Boolean = hasStart.get()

    //设备是否可发指令
    private fun isAvailable(): Boolean = operator.isAvailable()

    val id: Int get() = usbDevice.deviceId

    //关闭设备
    fun close() {
        if (isOpened()) h.sendEmptyMessage(MSG_CLOSE)
    }

    private fun log(log: String) {
        Logger.d(TAG, "<<------------$log")
    }

    fun addUBCDevice(mac: String, w_num: Int, r_num: Int, isAuto: Boolean, tag: Any?): Boolean {
        if (isOpened()) {
            val devicesCount: Int
            synchronized(deviceSync) {
                devicesCount = devices.size
            }
            if (devicesCount < MAX_DEVICE_SIZE - 1) {
                h.sendMessage(h.obtainMessage(MSG_ADD_DEVICE, UBCDevice(mac, w_num, r_num, isAuto, tag)))
                return true
            }
        }
        return false
    }

    //添加设备列表, 返回true代表还有剩余的设备可以给下个UBCDispatcher添加
    fun addUBCDeviceList(deviceList: MutableList<UBCDevice>): Boolean {
        if (isOpened() && deviceList.isNotEmpty()) {
            val devicesCount: Int
            synchronized(deviceSync) {
                devicesCount = devices.size
            }
            var left = MAX_DEVICE_SIZE - devicesCount - 1
            if (left > 0) {
                val iterator = deviceList.iterator()
                val deviceAddList = LinkedList<UBCDevice>()
                while (left-- > 0 && iterator.hasNext()) {
                    deviceAddList.add(iterator.next())
                    iterator.remove()
                }
                h.sendMessage(h.obtainMessage(MSG_ADD_DEVICE_LIST, deviceAddList))
            }
        }
        return deviceList.isNotEmpty()
    }

    fun restart() {
        if (isOpened()) h.sendEmptyMessage(MSG_RESTART)
    }

    fun connect() {
        if (isOpened()) h.sendEmptyMessage(MSG_CONNECT_ALL)
    }

    //只断开不会删除设备
    fun disconnect() {
        if (isOpened()) h.sendEmptyMessage(MSG_DISCONNECT_ALL)
    }

    //取消断开重连, 用于自动重连
    fun cancelAndClearAuto() {
        if (isOpened()) h.sendEmptyMessage(MSG_CANCEL_ALL)
    }

    //清空设备并断开, 此种只能用于非自动重加模式
    fun clear() {
        if (isOpened()) h.sendEmptyMessage(MSG_CLEAR)
    }

    fun pushHexCommand(hexCommand: String, timeout: Int) {
        if (isOpened()) h.sendMessage(h.obtainMessage(MSG_HEX_COMMAND_ALL, timeout, 0, hexCommand))
    }

    fun pushHexCommand(mac: String, hexCommand: String, timeout: Int) {
        if (isOpened()) {
            val macAndHexCommand = arrayOf(mac, hexCommand)
            h.sendMessage(h.obtainMessage(MSG_HEX_COMMAND, timeout, 0, macAndHexCommand))
        }
    }

    fun pushHexCommand(macs: List<String>, hexCommand: String, timeout: Int) {
        if (isOpened()) {
            val macsAndHexCommand = arrayOf(macs, hexCommand)
            h.sendMessage(h.obtainMessage(MSG_HEX_COMMAND_MULTIPLE, timeout, 0, macsAndHexCommand))
        }
    }

    fun scan() {
        if (isOpened()) h.sendMessage(h.obtainMessage(MSG_SCANNER, true))
    }

    fun stopScan() {
        if (isOpened()) h.sendMessage(h.obtainMessage(MSG_SCANNER, false))
    }

    //关闭, Thread
    private fun mineClose() {
        devices.forEach { (_, device) -> device.reset() }
        connectedDevices.clear()
        try {
            operator.close()
        } catch (_: Exception) {
        }
        looper.quit()
    }

    //添加设备 Thread
    private fun mineAddUBCDevice(device: UBCDevice) {
        if (macs.contains(device.mac)) {
            log("addDevice(${usbDevice.deviceId}) repeat mac = ${device.mac}")
            return
        }
        macs.add(device.mac)
        synchronized(deviceSync) {
            devices[device.mac] = device
        }
    }

    private fun mineAddUBCDeviceList(deviceList: List<UBCDevice>) {
        deviceList.forEach { mineAddUBCDevice(it) }
    }

    //重启, Thread
    private fun mineRestart() {
        log("try reset($id) and clear devices isAvailable = ${isAvailable()}")
        operator.restart()
        synchronized(deviceSync) {
            devices.clear()
        }
        macs.clear()

        /*if (isAvailable()) {
            operator.restart.restart()
            synchronized(deviceSync) {
                devices.clear()
            }
            connectedDevices.clear()
            macs.clear()
        } else {
            Logger.d(TAG, "try reset(${usbDevice.deviceId}) and clear devices but is not available...")
        }*/
    }

    //取消并断开重连
    private fun mineCancelAuto() {
        log("try cancelAuto($id) and clear devices isAvailable = ${isAvailable()}")
        operator.cancelAndClear()
        synchronized(deviceSync) {
            devices.clear()
        }
        connectedDevices.clear()
        macs.clear()
    }

    //连接全部设备 Thread
    private fun mineConnectAll() {
        if (isAvailable()) {
            val deviceList = LinkedList<UBCDevice>().apply {
                devices.forEach { (_, u) -> if (u.needConnected()) add(u) }
            }
            if (deviceList.isNotEmpty()) {
                operator.pushConnectPendingDevices(deviceList)
            } else {
                log("try connectAll($id) but needConnectList is empty!!!")
            }
        } else {
            log("try connectAll($id) but is not available!!!")
        }
    }

    //所有设备发指令
    private fun minePushHexCommand(hexCommand: String, timeout: Int) {
        if (!isAvailable()) {
            log("try pushHexCommand($id), $hexCommand but is not available !!!")
            return
        }
        val deviceList = LinkedList<UBCDevice>().apply {
            devices.forEach { (_, u) -> if (u.mayWrite()) add(u) }
        }
        if (deviceList.isNotEmpty()) {
            operator.pushWritePendingDevices(deviceList, hexCommand, timeout)
        } else {
            log("try pushHexCommand($id) $hexCommand but deviceList is empty!!!")
        }
    }

    //单台MAC发送
    private fun minePushHexCommand(mac: String, hexCommand: String, timeout: Int) {
        if (!isAvailable()) {
            log("try pushHexCommand($id) mac = $mac, $hexCommand but is not available !!!")
            return
        }
        val device = devices[mac]
        if (device != null && device.mayWrite()) {
            operator.pushWritePendingDevice(device, hexCommand, timeout)
        } else {
            log("try pushHexCommand($id) mac = $mac,  $hexCommand but device null or may not write!!!")
        }
    }

    //多台MAC发送
    private fun minePushHexCommand(macs: List<String>, hexCommand: String, timeout: Int) {
        if (!isAvailable()) {
            log("try pushHexCommand($id) mac = $macs, $hexCommand but is not available !!!")
            return
        }
        val deviceList = macs.mapNotNull { mac -> devices[mac]?.takeIf { it.mayWrite() } }
        if (deviceList.isNotEmpty()) {
            operator.pushWritePendingDevices(deviceList, hexCommand, timeout)
        } else {
            log("try pushHexCommand($id) mac = $macs, $hexCommand but deviceList is empty!!!")
        }
    }

    //断开链接 Thread
    private fun mineDisconnect() {
        if (isAvailable()) {
            operator.pushSimpleCommand(COMMAND_DISCONNECT)
        } else {
            log("try disconnect($id) but is not available !!!")
        }
    }

    //断开并清空 Thread
    private fun mineClear() {
        if (isAvailable()) {
            operator.pushSimpleCommand(COMMAND_DISCONNECT)
            synchronized(deviceSync) {
                devices.clear()
            }
            connectedDevices.clear()
            macs.clear()
        } else {
            log("try disconnect($id) but is not available !!!")
        }
    }

    //扫描或者停止扫描  Thread
    private fun mineScan(isStart: Boolean) {
        if (isAvailable()) {
            if (isStart) {
                operator.pushSimpleCommand(COMMAND_START_SCAN)
            } else {
                operator.pushSimpleCommand(COMMAND_STOP_SCAN)
            }
        } else {
            log("try scan isStart = $isStart but is not available !!!")
        }
    }

    override fun run() {
        log("UBCDispatcher $id init and run loop")
        Looper.prepare()
        looper = Looper.myLooper()!!
        h = H(looper)
//        hasStart.set(true)
        operator = openDevice(usbDevice)
        hasStart.set(operator.openSuccess)
        Looper.loop()
        hasStart.set(false)
        log("UBCDispatcher $id quit")
    }

    //设置服务UUID让设备处于服务状态
    internal fun serving() {
        if (isAvailable()) {
            val deviceList = LinkedList<UBCDevice>().apply {
                devices.forEach { (_, u) -> if (u.needServing()) add(u) }
            }
            if (deviceList.isNotEmpty()) {
                operator.pushServingPendingDevices(deviceList)
            } else {
                log("try serving($id) but need serving is empty!!!")
            }
        } else {
            log("try serving($id) but is not available!!!")
        }
    }

    internal fun onConnected(mac: String, id: Int) {
        val device = devices[mac]
        if (device != null) {
            device.connected(id)
            connectedDevices[id] = device
            callbackExecute.sendConnected(device, isAssistDispatcher)
        }
    }

    internal fun onDisconnected(mac: String) {
        val device = devices[mac]
        if (device != null) {
            connectedDevices.remove(device.id)
            device.disconnected()
            callbackExecute.sendDisconnected(device, isAssistDispatcher)
        }
    }

    internal fun onReceived(id: Int, data: String) {
        connectedDevices.get(id)?.also { callbackExecute.sendReceived(it, data, isAssistDispatcher) }
    }

    internal fun onReceivedHex(id: Int, bytes: ByteArray, start: Int, size: Int) {
        if (connectedDevices.get(id) == null) {
            Logger.i(TAG, "***************** device null  $id   **************")
        }
        connectedDevices.get(id)?.also { callbackExecute.sendReceivedHex(it, bytes, start, size, isAssistDispatcher) }
    }

    internal fun onScan(result: UBCScanResult) {
        callbackExecute.sendScan(result)
    }

    private fun openDevice(device: UsbDevice): UBCOperator {
        val serialPort = UsbSerialDevice.createUsbSerialDevice(device, usbManager.openDevice(device))
        if (serialPort.open()) {
            val operator = UBCOperator(serialPort, this, true)
            serialPort.setBaudRate(baudRate)
            serialPort.setDataBits(dataBits)
            serialPort.setStopBits(stopBits)
            serialPort.setParity(parity)
            serialPort.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF)
            //上面是设置些常用参数
            val reader = factory.create(UBCHandler(operator, looper), id)
            serialPort.read(reader)
            serialPort.getCTS(reader)
            serialPort.getDSR(reader)
            return operator
        } else {
            Logger.e(TAG, "open ${device.deviceName} err ************* ================= *************")
            return UBCOperator(serialPort, this, false)
        }
    }

    //由于此Handle只属于此线程的只处理此线程的消息处理, 因此不需要考虑弱引用, 可以参考android源码中的ActivityThread
    inner class H(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_CLOSE -> mineClose()
                MSG_ADD_DEVICE -> mineAddUBCDevice(msg.obj as UBCDevice)
                MSG_ADD_DEVICE_LIST -> mineAddUBCDeviceList(msg.obj as List<UBCDevice>)
                MSG_RESTART -> mineRestart()
                MSG_CONNECT_ALL -> mineConnectAll()
                MSG_DISCONNECT_ALL -> mineDisconnect()
                MSG_CANCEL_ALL -> mineCancelAuto()
                MSG_CLEAR -> mineClear()
                MSG_HEX_COMMAND_ALL -> minePushHexCommand(msg.obj as String, msg.arg1)
                MSG_HEX_COMMAND -> {
                    val macAndHexCommand = msg.obj as Array<String>
                    minePushHexCommand(macAndHexCommand[0], macAndHexCommand[1], msg.arg1)
                }
                MSG_HEX_COMMAND_MULTIPLE -> {
                    val macsAndHexCommand = msg.obj as Array<*>
                    minePushHexCommand(macsAndHexCommand[0] as List<String>, macsAndHexCommand[1] as String, msg.arg1)
                }
                MSG_SCANNER -> mineScan(msg.obj as Boolean)
            }
        }
    }
}